/*
 Zii EGGxplorer - A simple Zii file browser

 Copyright (C) 2009 - 2010 Claudio Corti

 Implementation file for MainWindow class, derived from CSkinWindowMultiContainer; this is the
 browsing window.
 */

// Unix standard headers
#include <errno.h>
#include <libgen.h>
#include <sys/types.h>

// C++ standard library
#include <cstring>
#include <cctype>
#include <fstream>
#include <list>

// Plaszma library
#include <skin.h>

// Application headers
#include "MainWindow.h"
#include "EGGxplorer.h"
#include "PathInfo.h"
#include "resources.h"

using namespace std;

// Constructor
MainWindow::MainWindow(CLayoutStudio *Layout, CSkinApp *Application) :
	list_view(NULL), images(NULL), denied_images(NULL), error_images(NULL)
{
	layout_studio = Layout;
	app = Application;
	display_state[0] = display_state[1] = display_state[3] = 0;

	// Load item images from resource file: denied_images, error_images and link images are
	// initialized as NULL and will be created only when the first image of that type occur;
	// this way, we save some startup time.
	image_res = layout_studio->GetResourceHandle(CRESOURCE_IMAGE);
	if (image_res)
	{
		images = new Image *[2 * ImagesNumber];
		denied_images = new Image *[2 * ImagesNumber];
		error_images = new Image *[2 * ImagesNumber];
		int i = 0;
		for (; i < ImagesNumber; ++i)
		{
			images[i] = (Image *) image_res->Load(images_names[i]);
			denied_images[i] = NULL;
			error_images[i] = NULL;
		}
		// Linked file images
		for (; i < 2 * ImagesNumber; ++i)
		{
			images[i] = NULL;
			denied_images[i] = NULL;
			error_images[i] = NULL;
		}
		link_symbol = (Image *) image_res->Load(IDI_LINK);
		denied_symbol = (Image *) image_res->Load(IDI_DENIED);
		error_symbol = (Image *) image_res->Load(IDI_ERROR);
	}

	current_path = new PathInfo((char *) "/");
}

// Destructor
MainWindow::~MainWindow()
{
	// Unload resources
	if (image_res)
	{
		int i = 0;
		for (; i < ImagesNumber; ++i)
		{
			image_res->Unload(images[i]);
			if (denied_images[i] != NULL)
				delete denied_images[i];
			if (error_images[i] != NULL)
				delete error_images[i];
		}
		for (; i < 2 * ImagesNumber; ++i)
		{
			if (images[i] != NULL)
				delete images[i];
			if (denied_images[i] != NULL)
				delete denied_images[i];
			if (error_images[i] != NULL)
				delete error_images[i];
		}
		image_res->Unload(link_symbol);
		image_res->Unload(denied_symbol);
		image_res->Unload(error_symbol);
	}
}

// Handle SD notification
void MainWindow::OnSDNotification(CUserEvent *SDEvent)
{
	if (Desktop_GetExternalStorageInfo(SDEvent, &sd_info))
	{
		if (sd_info.mIsMounted)
			tool_bar->EnableButton((char *) "SDButton");
		else
			tool_bar->DisableButton((char *) "SDButton");
	}
	else
		tool_bar->DisableButton((char *) "SDButton");

	if (strstr(current_path->GetPathName(), "/media/"))
	{
		delete current_path;
		current_path = new PathInfo((char *) "/media");
	}
	if (!strcmp(current_path->GetPathName(), "/") || !strcmp(current_path->GetPathName(), "/media"))
		UpdateWidgets();
}

// Called whenever a child widget is deleted
void MainWindow::OnDeleteChildWidget(CSkinWidget *widget)
{
	if (widget != NULL)
		layout_studio->DestroyWidget(widget, NULL);
}

// Called when the window is created
void MainWindow::OnCreate()
{
	LayoutWidgetParamType CreationParam;

	// Create the TitleBar and add it to the window
	CreationParam.mpRect = NULL;
	CreationParam.mpParentWindow = this;
	CreationParam.mpParentWidget = NULL;
	CreationParam.mState = SKIN_DEFAULT;
	CTitleBar * title_bar;
	title_bar = (CTitleBar *) layout_studio->CreateWidget(NAME_MAINWINDOW,
			NAME_TITLEBAR, &CreationParam);
	if (title_bar == NULL)
		return;
	PushWidget(title_bar);

	// Create the TwoLineListBox and add it to the window
	CreationParam.mpRect = NULL;
	CreationParam.mpParentWindow = this;
	CreationParam.mpParentWidget = NULL;
	CreationParam.mState = SKIN_DEFAULT;
	list_view = (CTwoLineListBox *) layout_studio->CreateWidget(
			NAME_MAINWINDOW, NAME_LISTVIEW, &CreationParam);
	if (list_view == NULL)
		return;
	PushWidget(list_view);

	// Create the ToolBar and add it to the window
	CreationParam.mpRect = NULL;
	CreationParam.mpParentWindow = this;
	CreationParam.mpParentWidget = NULL;
	CreationParam.mState = SKIN_DEFAULT;
	tool_bar = (CToolBar *) layout_studio->CreateWidget(NAME_MAINWINDOW,
			NAME_TOOLBAR, &CreationParam);
	if (tool_bar == NULL)
		return;
	PushWidget(tool_bar);

	tool_bar->DisableButton((char *) "BackButton");
	tool_bar->DisableButton((char *) "NextButton");
	tool_bar->DisableButton((char *) "UpButton");
	tool_bar->DisableButton((char *) "OptionsButton");
	if (Desktop_GetExternalStorageState(&sd_info))
	{
		if (sd_info.mIsMounted)
			tool_bar->EnableButton((char *) "SDButton");
		else
			tool_bar->DisableButton((char *) "SDButton");
	}

	UpdateWidgets();
}

// Called when an item in the list is selected
EventReturnType MainWindow::OnSelChange(CSkinWidget* widget)
{
	bool update = false;
	switch (widget->GetIdentifier())
	{
	case ID_LISTVIEW:
	{
		PathInfo *element;
		list<PathInfo *>::iterator i;
		int item_index = list_view->GetSelectedIndex(), j;

		if (current_path->GetError() == NULL)
		{
			CTwoLineListItem *item = new CTwoLineListItem();

			list_view->GetItem(0, item);

			if (item_index < folders.size())
			{
				for (i = folders.begin(), j = 0; j < item_index; ++i, ++j)
					;
				element = *i;
			}
			else
			{
				item_index -= folders.size();
				for (i = files.begin(), j = 0; j < item_index; ++i, ++j)
					;
				element = *i;
			}

			j = 0;
			if (element->TypeIs(SymbolicLink))
			{
				char *linked_path = element->GetLinkedPath();
				if (linked_path != NULL)
				{
					element = new PathInfo(linked_path);
					j = 1;
				}
			}

			if (element->TypeIs(Directory))
			{
				delete current_path;
				current_path = new PathInfo(element->GetPathName());
				if (j)
					delete element;
				update = true;
			}
		}
		break;
	}
	}
	if (update)
	{
		list_view->GetDisplayState(&display_state[0], &display_state[1],
				&display_state[2]);

		browse_current_path();
		show_list_view();

		list_view->SetSelectedIndex(-1);
		list_view->RequestPaint(NULL);
		list_view->Update();

		if (!strcmp(current_path->GetBaseName(), "/"))
			tool_bar->DisableButton((char *) "UpButton");
		else
			tool_bar->EnableButton((char *) "UpButton");
	}
	return EVENT_RETURN_CONSUMED;
}

// Called when a toolbar button is clicked
EventReturnType MainWindow::OnClick(CSkinWidget *widget)
{
	bool update = false;

	switch (widget->GetIdentifier())
	{
	case ID_BACK_BUTTON:
		break;
	case ID_NEXT_BUTTON:
		break;
	case ID_UP_BUTTON:
	{
		PathInfo *old = current_path;
		current_path = new PathInfo(old->GetParentName());
		delete old;
		update = true;
		break;
	}
	case ID_ROOT_BUTTON:
	{
		PathInfo *old = current_path;
		current_path = new PathInfo((char *) "/");
		delete old;
		update = true;
		break;
	}
	case ID_HOME_BUTTON:
	{
		PathInfo *old = current_path;
		current_path = new PathInfo((char *) "/home/Public");
		delete old;
		update = true;
		break;
	}
	case ID_SD_BUTTON:
	{
		if (sd_info.mIsMounted)
		{
			PathInfo *old = current_path;
			current_path = new PathInfo(sd_info.mpMountPoint);
			delete old;
			update = true;
		}
		break;
	}
	case ID_OPTIONS_BUTTON:
		break;
	}
	if (update)
		UpdateWidgets();
	return EVENT_RETURN_CONSUMED;
}

// Get a file image
Image *MainWindow::get_image(int err_code, int type)
{
	Image *image;

	if (type >= SymbolicLink && images[type] == NULL)
		create_link_image(type);

	if (!err_code)
		image = images[type];
	else if (err_code == EACCES)
	{
		if (denied_images[type] == NULL)
			create_image(err_code, type);
		image = denied_images[type];
	}
	else
	{
		if (error_images[type] == NULL)
			create_image(err_code, type);
		image = error_images[type];
	}
	return image;
}

// Create a link file image
void MainWindow::create_link_image(int type)
{
	CRect source(0, 0, 26, 26);
	CRect dest(44, 0, 26, 26);
	images[type] = Image::Copy(app->GetSequencer(), link_symbol, images[type
			- SymbolicLink], source, dest);
}

// Create an error or denied file image
void MainWindow::create_image(int err_code, int type)
{
	CRect source(0, 0, 30, 30);
	CRect dest(40, 40, 30, 30);
	if (err_code == EACCES)
		denied_images[type] = Image::Copy(app->GetSequencer(), denied_symbol,
				images[type], source, dest);
	else
		error_images[type] = Image::Copy(app->GetSequencer(), error_symbol,
				images[type], source, dest);

}

// Add a file to the list
void MainWindow::add_file_to_list(PathInfo *element)
{
	CTwoLineListItem *item;
	CImage *image;
	char element_size[24], *time;
	char status = 0;

	item = new CTwoLineListItem();
	item->SetText(element->GetBaseName());
	int err_code;
	if (element->GetError(&err_code) != NULL)
		image = get_image(err_code, element->GetType());
	else
		image = get_image(0, element->GetType());
	item->SetImage(image);
	if (element->TypeIs(Directory))
		sprintf(element_size, "%d", element->GetSize());
	else
		get_pretty_size(element->GetSize(), element_size);
	item->SetLeftSubText(element_size);
	time = element->GetAccessTime((char *) "%a %d %b %Y, %H:%M:%S");
	if (time != NULL)
		item->SetRightSubText(time);
	list_view->AddItem(item);

	delete time;
	delete item;
}

// Fill folders and files list
void MainWindow::browse_current_path()
{
	PathInfo *element;
	list<PathInfo *>::iterator i;
	char element_size[24], *time;

	for (i = folders.begin(); i != folders.end(); ++i)
		if (*i != NULL)
			delete *i;
	for (i = files.begin(); i != files.end(); ++i)
		if (*i != NULL)
			delete *i;

	folders.clear();
	files.clear();

	//title_bar->SetText(current_path->GetPathName());

	if (current_path->QueryInfo())
	{
		while (element = current_path->Next())
		{
			element->QueryInfo(); // infos about file collected here; if error, GetError != NULL
			if (element->TypeIs(Directory))
				folders.push_back(element);
			else
				files.push_back(element);
		}
		PathInfoPtrNameComparator sorter;
		folders.sort(sorter);
		files.sort(sorter);
	}
}

// Fill the listbox
void MainWindow::show_list_view()
{
	CTwoLineListItem *item;
	int err_code;

	if (list_view->GetTotalItemCount())
		list_view->RemoveAllItems();

	// test if error occurred during browse_current_path execution
	char *err_msg = current_path->GetError(&err_code);
	if (err_msg == NULL)
	{
		list<PathInfo *>::iterator i;

		for (i = folders.begin(); i != folders.end(); ++i)
			add_file_to_list(*i);
		for (i = files.begin(); i != files.end(); ++i)
			add_file_to_list(*i);
	}
	else
	{
		char status = 0;
		Image *image;

		item = new CTwoLineListItem();
		item->SetText((char *) "Error!");
		item->SetLeftSubText(err_msg);
		image = get_image(err_code, current_path->GetType());
		item->SetImage(image);
		list_view->AddItem(item);
		delete item;
	}
}

void MainWindow::get_pretty_size(int size, char *str)
{
	if (size < 500)
		sprintf(str, "%d B", size);
	else if (size < 500000)
		sprintf(str, "%3.1f KB", size / 1024.0);
	else if (size < 500000000)
		sprintf(str, "%3.1f MB", size / 1048576.0);
	else
		sprintf(str, "%3.1f GB", size / 1073741824.0);
}

void MainWindow::UpdateWidgets()
{
	browse_current_path();
	show_list_view();

	list_view->SetSelectedIndex(-1);
	list_view->RequestPaint(NULL);
	list_view->Update();

	list_view->SetDisplayState(display_state[0], display_state[1],
			display_state[2]);

	if (!strcmp(current_path->GetBaseName(), "/"))
		tool_bar->DisableButton((char *) "UpButton");
	else
		tool_bar->EnableButton((char *) "UpButton");

	//title_bar->SetText(current_path->GetPathName());
}
